﻿#region References
using System;
#endregion

namespace CustomsFramework
{
	internal class Decompressor
	{
		#region Decompression Tree
		private static readonly int[,] _huffmanTree = new int[256,2]
		{
			/*node*/ /*leaf0 leaf1*/
			/* 0*/ {2, 1}, /* 1*/ {4, 3}, /* 2*/ {0, 5}, /* 3*/ {7, 6}, /* 4*/ {9, 8}, /* 5*/ {11, 10}, /* 6*/ {13, 12}, /* 7*/
			{14, -256}, /* 8*/ {16, 15}, /* 9*/ {18, 17}, /* 10*/ {20, 19}, /* 11*/ {22, 21}, /* 12*/ {23, -1}, /* 13*/ {25, 24},
			/* 14*/ {27, 26}, /* 15*/ {29, 28}, /* 16*/ {31, 30}, /* 17*/ {33, 32}, /* 18*/ {35, 34}, /* 19*/ {37, 36}, /* 20*/
			{39, 38}, /* 21*/ {-64, 40}, /* 22*/ {42, 41}, /* 23*/ {44, 43}, /* 24*/ {45, -6}, /* 25*/ {47, 46}, /* 26*/ {49, 48}
			, /* 27*/ {51, 50}, /* 28*/ {52, -119}, /* 29*/ {53, -32}, /* 30*/ {-14, 54}, /* 31*/ {-5, 55}, /* 32*/ {57, 56},
			/* 33*/ {59, 58}, /* 34*/ {-2, 60}, /* 35*/ {62, 61}, /* 36*/ {64, 63}, /* 37*/ {66, 65}, /* 38*/ {68, 67}, /* 39*/
			{70, 69}, /* 40*/ {72, 71}, /* 41*/ {73, -51}, /* 42*/ {75, 74}, /* 43*/ {77, 76}, /* 44*/ {-111, -101}, /* 45*/
			{-97, -4}, /* 46*/ {79, 78}, /* 47*/ {80, -110}, /* 48*/ {-116, 81}, /* 49*/ {83, 82}, /* 50*/ {-255, 84}, /* 51*/
			{86, 85}, /* 52*/ {88, 87}, /* 53*/ {90, 89}, /* 54*/ {-10, -15}, /* 55*/ {92, 91}, /* 56*/ {93, -21}, /* 57*/
			{94, -117}, /* 58*/ {96, 95}, /* 59*/ {98, 97}, /* 60*/ {100, 99}, /* 61*/ {101, -114}, /* 62*/ {102, -105}, /* 63*/
			{103, -26}, /* 64*/ {105, 104}, /* 65*/ {107, 106}, /* 66*/ {109, 108}, /* 67*/ {111, 110}, /* 68*/ {-3, 112},
			/* 69*/ {-7, 113}, /* 70*/ {-131, 114}, /* 71*/ {-144, 115}, /* 72*/ {117, 116}, /* 73*/ {118, -20}, /* 74*/
			{120, 119}, /* 75*/ {122, 121}, /* 76*/ {124, 123}, /* 77*/ {126, 125}, /* 78*/ {128, 127}, /* 79*/ {-100, 129},
			/* 80*/ {-8, 130}, /* 81*/ {132, 131}, /* 82*/ {134, 133}, /* 83*/ {135, -120}, /* 84*/ {-31, 136}, /* 85*/
			{138, 137}, /* 86*/ {-234, -109}, /* 87*/ {140, 139}, /* 88*/ {142, 141}, /* 89*/ {144, 143}, /* 90*/ {145, -112},
			/* 91*/ {146, -19}, /* 92*/ {148, 147}, /* 93*/ {-66, 149}, /* 94*/ {-145, 150}, /* 95*/ {-65, -13}, /* 96*/
			{152, 151}, /* 97*/ {154, 153}, /* 98*/ {155, -30}, /* 99*/ {157, 156}, /* 100*/ {158, -99}, /* 101*/ {160, 159},
			/* 102*/ {162, 161}, /* 103*/ {163, -23}, /* 104*/ {164, -29}, /* 105*/ {165, -11}, /* 106*/ {-115, 166}, /* 107*/
			{168, 167}, /* 108*/ {170, 169}, /* 109*/ {171, -16}, /* 110*/ {172, -34}, /* 111*/ {-132, 173}, /* 112*/ {-108, 174}
			, /* 113*/ {-22, 175}, /* 114*/ {-9, 176}, /* 115*/ {-84, 177}, /* 116*/ {-37, -17}, /* 117*/ {178, -28}, /* 118*/
			{180, 179}, /* 119*/ {182, 181}, /* 120*/ {184, 183}, /* 121*/ {186, 185}, /* 122*/ {-104, 187}, /* 123*/ {-78, 188},
			/* 124*/ {-61, 189}, /* 125*/ {-178, -79}, /* 126*/ {-134, -59}, /* 127*/ {-25, 190}, /* 128*/ {-18, -83}, /* 129*/
			{-57, 191}, /* 130*/ {192, -67}, /* 131*/ {193, -98}, /* 132*/ {-68, -12}, /* 133*/ {195, 194}, /* 134*/ {-128, -55},
			/* 135*/ {-50, -24}, /* 136*/ {196, -70}, /* 137*/ {-33, -94}, /* 138*/ {-129, 197}, /* 139*/ {198, -74}, /* 140*/
			{199, -82}, /* 141*/ {-87, -56}, /* 142*/ {200, -44}, /* 143*/ {201, -248}, /* 144*/ {-81, -163}, /* 145*/
			{-123, -52}, /* 146*/ {-113, 202}, /* 147*/ {-41, -48}, /* 148*/ {-40, -122}, /* 149*/ {-90, 203}, /* 150*/
			{204, -54}, /* 151*/ {-192, -86}, /* 152*/ {206, 205}, /* 153*/ {-130, 207}, /* 154*/ {208, -53}, /* 155*/
			{-45, -133}, /* 156*/ {210, 209}, /* 157*/ {-91, 211}, /* 158*/ {213, 212}, /* 159*/ {-88, -106}, /* 160*/ {215, 214}
			, /* 161*/ {217, 216}, /* 162*/ {-49, 218}, /* 163*/ {220, 219}, /* 164*/ {222, 221}, /* 165*/ {224, 223}, /* 166*/
			{226, 225}, /* 167*/ {-102, 227}, /* 168*/ {228, -160}, /* 169*/ {229, -46}, /* 170*/ {230, -127}, /* 171*/
			{231, -103}, /* 172*/ {233, 232}, /* 173*/ {234, -60}, /* 174*/ {-76, 235}, /* 175*/ {-121, 236}, /* 176*/ {-73, 237}
			, /* 177*/ {238, -149}, /* 178*/ {-107, 239}, /* 179*/ {240, -35}, /* 180*/ {-27, -71}, /* 181*/ {241, -69}, /* 182*/
			{-77, -89}, /* 183*/ {-118, -62}, /* 184*/ {-85, -75}, /* 185*/ {-58, -72}, /* 186*/ {-80, -63}, /* 187*/ {-42, 242},
			/* 188*/ {-157, -150}, /* 189*/ {-236, -139}, /* 190*/ {-243, -126}, /* 191*/ {-214, -142}, /* 192*/ {-206, -138},
			/* 193*/ {-146, -240}, /* 194*/ {-147, -204}, /* 195*/ {-201, -152}, /* 196*/ {-207, -227}, /* 197*/ {-209, -154},
			/* 198*/ {-254, -153}, /* 199*/ {-156, -176}, /* 200*/ {-210, -165}, /* 201*/ {-185, -172}, /* 202*/ {-170, -195},
			/* 203*/ {-211, -232}, /* 204*/ {-239, -219}, /* 205*/ {-177, -200}, /* 206*/ {-212, -175}, /* 207*/ {-143, -244},
			/* 208*/ {-171, -246}, /* 209*/ {-221, -203}, /* 210*/ {-181, -202}, /* 211*/ {-250, -173}, /* 212*/ {-164, -184},
			/* 213*/ {-218, -193}, /* 214*/ {-220, -199}, /* 215*/ {-249, -190}, /* 216*/ {-217, -230}, /* 217*/ {-216, -169},
			/* 218*/ {-197, -191}, /* 219*/ {243, -47}, /* 220*/ {245, 244}, /* 221*/ {247, 246}, /* 222*/ {-159, -148}, /* 223*/
			{249, 248}, /* 224*/ {-93, -92}, /* 225*/ {-225, -96}, /* 226*/ {-95, -151}, /* 227*/ {251, 250}, /* 228*/
			{252, -241}, /* 229*/ {-36, -161}, /* 230*/ {254, 253}, /* 231*/ {-39, -135}, /* 232*/ {-124, -187}, /* 233*/
			{-251, 255}, /* 234*/ {-238, -162}, /* 235*/ {-38, -242}, /* 236*/ {-125, -43}, /* 237*/ {-253, -215}, /* 238*/
			{-208, -140}, /* 239*/ {-235, -137}, /* 240*/ {-237, -158}, /* 241*/ {-205, -136}, /* 242*/ {-141, -155}, /* 243*/
			{-229, -228}, /* 244*/ {-168, -213}, /* 245*/ {-194, -224}, /* 246*/ {-226, -196}, /* 247*/ {-233, -183}, /* 248*/
			{-167, -231}, /* 249*/ {-189, -174}, /* 250*/ {-166, -252}, /* 251*/ {-222, -198}, /* 252*/ {-179, -188}, /* 253*/
			{-182, -223}, /* 254*/ {-186, -180}, /* 255*/ {-247, -245}
		};
		#endregion

		private static int GetBit(int buf, int bit)
		{
			return (buf >> (bit - 1)) & 1;
		}

		public static bool Decompress(byte[] source, int sourceLength, byte[] destination, ref int destinationLength)
		{
			Array.Clear(destination, 0, destination.Length);
			destinationLength = 0;
			int node = 0, leaf = 0, leaf_value = 0, dest_pos = 0, bit_num = 8, src_pos = 0;

			while (src_pos < sourceLength)
			{
				leaf = GetBit(source[src_pos], bit_num);
				leaf_value = _huffmanTree[node, leaf];

				// all numbers below 1 (0..-256) are codewords
				// if the halt codeword has been found, skip this byte
				if (leaf_value == -256)
				{
					bit_num = 8;
					node = 0;
					src_pos++;
					var newsource = new byte[sourceLength - src_pos];
					Array.Copy(source, src_pos, newsource, 0, sourceLength - src_pos);
					source = newsource;
					destinationLength = dest_pos;
					return true;
				}
				else if (leaf_value < 1)
				{
					destination[dest_pos] = (byte)-leaf_value;
					leaf_value = 0;
					dest_pos++;
				}

				bit_num--;
				node = leaf_value;
				/* if its the end of the byte, go to the next byte */
				if (bit_num < 1)
				{
					bit_num = 8;
					src_pos++;
				}

				// check to see if the current codeword has no end
				// if not, make it an incomplete byte
				if (src_pos == sourceLength)
				{
					if (node != 0)
					{
						return false;
					}
				}
				/*if(obj != NULL && src_pos == *src_size && node)
                {
                obj->incomplete_byte = src[src_pos-1];
                obj->has_incomplete = 1;
                }*/
			}

			destinationLength = dest_pos;
			return false;
		}

		public static byte? DecompressFirstByte(byte[] source, int sourceLength)
		{
			int node = 0, leaf = 0, value = 0, bit = 8, index = 0;

			while (index < sourceLength)
			{
				leaf = GetBit(source[index], bit);
				value = _huffmanTree[node, leaf];

				if (value == -256)
				{
					bit = 8;
					node = 0;
					index++;

					continue;
				}
				else if (value < 1)
				{
					return (byte)-value;
				}

				bit--;
				node = value;

				if (bit < 1)
				{
					bit = 8;
					index++;
				}
			}

			return null;
		}

		public static void DecompressAll(byte[] src, int src_size, byte[] dest, ref int dest_size)
		{
			int node = 0, leaf = 0, leaf_value = 0, dest_pos = 0, bit_num = 8, src_pos = 0;

			while (src_pos < src_size)
			{
				leaf = GetBit(src[src_pos], bit_num);
				leaf_value = _huffmanTree[node, leaf];

				// all numbers below 1 (0..-256) are codewords
				// if the halt codeword has been found, skip this byte
				if (leaf_value == -256)
				{
					bit_num = 8;
					node = 0;
					src_pos++;
					continue;
				}
				else if (leaf_value < 1)
				{
					dest[dest_pos] = (byte)-leaf_value;
					leaf_value = 0;
					dest_pos++;
				}

				bit_num--;
				node = leaf_value;
				/* if its the end of the byte, go to the next byte */
				if (bit_num < 1)
				{
					bit_num = 8;
					src_pos++;
				}
				// check to see if the current codeword has no end
				// if not, make it an incomplete byte
				/*if(obj != NULL && src_pos == *src_size && node)
                {
                obj->incomplete_byte = src[src_pos-1];
                obj->has_incomplete = 1;
                }*/
			}

			dest_size = dest_pos;
			return;
		}
	}
}